package gregicadditions.recipes.impl;

import gregtech.api.capability.impl.FluidTankList;
import gregtech.api.gui.GuiTextures;
import gregtech.api.gui.ModularUI;
import gregtech.api.gui.resources.TextureArea;
import gregtech.api.gui.widgets.ProgressWidget;
import gregtech.api.recipes.RecipeBuilder;
import gregtech.api.recipes.RecipeMap;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraftforge.items.IItemHandlerModifiable;

import java.util.function.DoubleSupplier;

@MethodsReturnNonnullByDefault
public class RecipeMapAssemblyLine<R extends RecipeBuilder<R>> extends RecipeMap<R> {
	private TextureArea progressBarTexture;
	private ProgressWidget.MoveType moveType;

	public RecipeMapAssemblyLine(String unlocalizedName, int minInputs, int maxInputs, int minOutputs, int maxOutputs, int minFluidInputs, int maxFluidInputs, int minFluidOutputs, int maxFluidOutputs, R defaultRecipe) {
		super(unlocalizedName, minInputs, maxInputs, minOutputs, maxOutputs, minFluidInputs, maxFluidInputs, minFluidOutputs, maxFluidOutputs, defaultRecipe);
	}

	@Override
	public RecipeMapAssemblyLine<R> setProgressBar(TextureArea progressBar, ProgressWidget.MoveType moveType) {
		this.progressBarTexture = progressBar;
		this.moveType = moveType;
		super.setProgressBar(progressBar, moveType);
		return this;
	}

	@Override
	public ModularUI.Builder createUITemplate(DoubleSupplier progressSupplier, IItemHandlerModifiable importItems, IItemHandlerModifiable exportItems, FluidTankList importFluids, FluidTankList exportFluids) {
		ModularUI.Builder builder = new ModularUI.Builder(GuiTextures.BACKGROUND_EXTENDED, 176, 216) {
			@Override
			public ModularUI.Builder bindPlayerInventory(InventoryPlayer inventoryPlayer) {
				this.bindPlayerInventory(inventoryPlayer, 134);
				return this;
			}

		};
		builder.widget(new ProgressWidget(progressSupplier, 109, 22, 20, 20, this.progressBarTexture, this.moveType));
		this.addInventorySlotGroup(builder, importItems, importFluids, false);
		this.addInventorySlotGroup(builder, exportItems, exportFluids, true);
		return builder;
	}

	@Override
	protected void addInventorySlotGroup(ModularUI.Builder builder, IItemHandlerModifiable itemHandler, FluidTankList fluidHandler, boolean isOutputs) {
		int itemInputsCount = itemHandler.getSlots();
		int fluidInputsCount = fluidHandler.getTanks();
		boolean invertFluids = false;
		if (itemInputsCount == 0) {
			int tmp = itemInputsCount;
			itemInputsCount = fluidInputsCount;
			fluidInputsCount = tmp;
			invertFluids = true;
		}
		int[] inputSlotGrid = determineSlotsGrid(itemInputsCount);
		int itemSlotsToLeft = inputSlotGrid[0];
		int itemSlotsToDown = inputSlotGrid[1];
		int startInputsX = isOutputs ? 138 : 101 - itemSlotsToLeft * 18;
		int startInputsY = 32 - (int) (itemSlotsToDown / 2.0 * 18);
		for (int i = 0; i < itemSlotsToDown; i++) {
			for (int j = 0; j < itemSlotsToLeft; j++) {
				int slotIndex = i * itemSlotsToLeft + j;
				addSlot(builder, startInputsX + 18 * j, startInputsY + 18 * i, slotIndex, itemHandler, fluidHandler, invertFluids, isOutputs);
			}
		}
		if (fluidInputsCount > 0 || invertFluids) {
			if (itemSlotsToDown >= fluidInputsCount) {
				int startSpecX = isOutputs ? startInputsX + itemSlotsToLeft * 18 : startInputsX - 18;
				for (int i = 0; i < fluidInputsCount; i++) {
					addSlot(builder, startSpecX, startInputsY + 18 * i, i, itemHandler, fluidHandler, !invertFluids, isOutputs);
				}
			} else {
				int startSpecY = startInputsY + itemSlotsToDown * 18;
				int offsetX = isOutputs ? 0 : 18;
				for (int i = 0; i < fluidInputsCount; i++) {
					addSlot(builder, startInputsX - offsetX + 18 * i, startSpecY, i, itemHandler, fluidHandler, !invertFluids, isOutputs);
				}
			}
		}
	}

	protected static int[] determineSlotsGrid(int itemInputsCount) {
		int itemSlotsToLeft = 0;
		int itemSlotsToDown = 0;
		double sqrt = Math.sqrt(itemInputsCount);
		if (sqrt % 1 == 0) { //check if square root is integer
			//case for 1, 4, 9 slots - it's square inputs (the most common case)
			itemSlotsToLeft = itemSlotsToDown = (int) sqrt;
		} else if (itemInputsCount % 3 == 0) {
			//case for 3 and 6 slots - 3 by horizontal and i / 3 by vertical (common case too)
			itemSlotsToDown = itemInputsCount / 3;
			itemSlotsToLeft = 3;
		} else if (itemInputsCount % 2 == 0) {
			//case for 2 inputs - 2 by horizontal and i / 3 by vertical (for 2 slots)
			itemSlotsToDown = itemInputsCount / 2;
			itemSlotsToLeft = 2;
		}
		return new int[] { itemSlotsToLeft, itemSlotsToDown };
	}
}